package while_lang.ast.exps;

import java.util.*;

import while_lang.ast.Ref;
import while_lang.semantics.Declaration;
import while_lang.semantics.State;
import while_lang.semantics.SymbolTable;
import while_lang.semantics.Type;

/** Nodo del AST que representa una variable.
 *  @author Leonardo Val
 */
public class Variable extends Ref {
	public static final String[] IDS = "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z".split(",");
	
	public final String id;
	
	public Variable(String id) {
		if (id.length() < 1 || !Character.isJavaIdentifierStart(id.charAt(0))) {
			throw new Error("Invalid identifier '"+ id +"'.");
		}
		this.id = id;
	}
	
	@Override public String unparse() {
		return id;
	}

	@Override public Object evaluate(State state) {
		return state.variable(id).value();
	}
	
	@Override public Object assign(State state, Object value) {
		return state.variable(id).assign(value);
	}
	
	@Override public Type checkTypes(SymbolTable table) {
		throw new Error("Variable.checkTypes no está implementado.");
	}
	
	@Override public boolean checkAssignment(SymbolTable table, Type type) {
		/* Aquí hay que verificar que el tipo del parámetro type sea 
		 * compatible con el de la variable. De ser así se debe marcar 
		 * la declaración como que tiene un valor definido.
		 */
		
		Declaration d = table.variable(this.id);
		d.hasValue = true;
		
		if(!d.type.isCompatible(type)){
			table.addError("ERROR : La variable"  + this.id + " no es compatible con el tipo " + type.unparse() );
		}
		return true;
		
	}
	
	public static Variable generate(Random random, int min, int max) {
		return new Variable(Variable.IDS[random.nextInt(Variable.IDS.length)]);
	}
	
	@Override public String toString() {
		return "Variable("+ id +")";
	}

	@Override public int hashCode() {
		return 31 + ((id == null) ? 0 : id.hashCode());
	}

	@Override public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Variable other = (Variable) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

}
